﻿using System.Net;
using T0yK4T.Configuration;

namespace ToyChat.Server
{
    /// <summary>
    /// Container class for settings
    /// </summary>
    public class ServerSettings : IServerSettings
    {
        private static ConfigurableValue<string> serverName = Config.GetValue<string>("servername", new StringConverter(), "noname");
        private static ConfigurableValue<IPAddress> localAddress = Config.GetValue<IPAddress>("localaddress", new IPAddressConverter(), IPAddress.Any);
        private static ConfigurableValue<int> localPort = Config.GetValue<int>("localport", new Int32Converter(), 5000);
        private static ConfigurableValue<string> databaseHost = Config.GetValue<string>("databasehost", new StringConverter(), "localhost");
        private static ConfigurableValue<string> databaseName = Config.GetValue<string>("databasename", new StringConverter(), "ToyChat");
        private static ConfigurableValue<string> databaseUser = Config.GetValue<string>("databaseuser", new StringConverter(), "");
        private static ConfigurableValue<string> databasePass = Config.GetValue<string>("databasepass", new StringConverter(), "");
        private static ConfigurableValue<int> databasePort = Config.GetValue<int>("databaseport", new Int32Converter(), 27017);
        private static ConfigurableValue<bool> requireLogin = Config.GetValue<bool>("requirelogin", new BooleanConverter(), true);
        private static ConfigurableValue<bool> allowDBSwitch = Config.GetValue<bool>("allowdbswitch", new BooleanConverter(), false);

        /// <summary>
        /// This event is fired whenever a settings property has changed
        /// </summary>
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Gets or Sets the name of the server
        /// </summary>
        public string ServerName
        {
            get
            {
                return serverName.TValue;
            }
            set
            {
                this.SetProperty(serverName, value);
            }
        }

        /// <summary>
        /// Gets or Sets the local endpoint to bind the listening socket to
        /// <remarks>Please note that setting this value will generate 2 (TWO) <see cref="PropertyChanged"/> events being fired (one for the address, and one for the port)</remarks>
        /// </summary>
        public IPEndPoint LocalEndpoint
        {
            get
            {
                return new IPEndPoint(localAddress, localPort);
            }
            set
            {
                this.SetProperty(localAddress, value.Address);
                this.SetProperty(localPort, value.Port);
            }
        }

        /// <summary>
        /// Gets or Sets the database server's hostname / ip address
        /// </summary>
        public string DatabaseHost
        {
            get
            {
                return databaseHost;
            }
            set
            {
                this.SetProperty(databaseHost, value);
            }
        }

        /// <summary>
        /// Gets or Sets the database name
        /// </summary>
        public string DatabaseName
        {
            get
            {
                return databaseName;
            }
            set
            {
                this.SetProperty(databaseName, value);
            }
        }

        /// <summary>
        /// Gets or Sets the username to use when connecting to the database
        /// </summary>
        public string DatabaseUser
        {
            get
            {
                return databaseUser;
            }
            set
            {
                this.SetProperty(databaseUser, value);
            }
        }

        /// <summary>
        /// Gets or Sets the password to use when connecting to the database (This is not secure!)
        /// </summary>
        public string DatabasePass
        {
            get
            {
                return databasePass;
            }
            set
            {
                this.SetProperty(databasePass, value);
            }
        }

        /// <summary>
        /// Gets or Sets the port to connect to when connecting to the database server
        /// </summary>
        public int DatabasePort
        {
            get
            {
                return databasePort;
            }
            set
            {
                this.SetProperty(databasePort, value);
            }
        }

        /// <summary>
        /// Gets or Sets a value indicating whether or not this server requires a user account / login to connect to.
        /// </summary>
        public bool RequireLogin
        {
            get
            {
                return requireLogin;
            }
            set
            {
                this.SetProperty(requireLogin, value);
            }
        }

        /// <summary>
        /// Crude validation of settings (Only checks for null-references)
        /// </summary>
        /// <returns></returns>
        public bool Validate()
        {
            bool ret = !string.IsNullOrEmpty(this.ServerName);
            ret &= this.LocalEndpoint != null;

            ret &= this.DatabaseHost != null;
            ret &= this.DatabaseName != null;
            ret &= this.DatabasePort > 0 && this.DatabasePort <= ushort.MaxValue;

            return ret;
        }

        /// <summary>
        /// Gets or Sets a value indicating whether or not the <see cref="IServer.GlobalDBHandler"/> is allowed to be replaced
        /// </summary>
        public bool AllowDBSwitch
        {
            get
            {
                return allowDBSwitch;
            }
            set
            {
                this.SetProperty(allowDBSwitch, value);
            }
        }

        /// <summary>
        /// Shortcut for setting <see cref="ConfigurableValue{T}.Value"/> and calling .Store()
        /// </summary>
        /// <typeparam name="T">The type of the <see cref="ConfigurableValue{T}"/></typeparam>
        /// <param name="property">The property to set the value on</param>
        /// <param name="value">The value to set</param>
        private void SetProperty<T>(ConfigurableValue<T> property, T value)
        {
            property.Value = value;
            property.Store();
            this.OnPropertyChanged(property.Key);
        }

        /// <summary>
        /// Called internally to fire the <see cref="PropertyChanged"/> event
        /// </summary>
        /// <param name="propertyName">The name of the property that changed</param>
        private void OnPropertyChanged(string propertyName)
        {
            if (this.PropertyChanged != null)
                this.PropertyChanged.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }
    }
}